package org.bjf.modules.core.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.bjf.exception.CommMsgCode;
import org.bjf.exception.ServiceException;
import org.bjf.modules.core.bean.Query;
import org.bjf.modules.core.web.core.PageVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.*;

/**
 * 服务基类，封装了基本的方法
 *
 * @param <M> mybatis mapper
 * @param <T> entity
 * @author bjf
 * @desc 方法默认没有使用@Transactional标记事务, 如要使用事务，建议在自己的service里面控制
 * @date 2017/11/27
 */
@Slf4j
public class BaseService<M extends BaseMapper<T>, T, Q extends Query> {

  private static final int IN_SIZE = 1000;

  @Autowired
  protected M baseMapper;

  /**
   * 新增
   */
  public boolean add(T obj) {
    Integer affCnt = baseMapper.insert(obj);
    return affCnt > 0;
  }

  public void addBatch(Collection<T> list) {
    this.addBatch(list, 1000);
  }

  /**
   * 新增(批量)
   */
  @Transactional(rollbackFor = Exception.class)
  public void addBatch(Collection<T> list, int batchSize) {
    if (CollectionUtils.isEmpty(list)) {
      log.warn("批量插入的集合为空");
      return;
    }
    if (batchSize < 1) {
      batchSize = 1000;
    }

    Class tClz = ReflectionKit.getSuperClassGenericType(getClass(), 1);
    SqlSessionFactory sqlSessionFactory = GlobalConfigUtils.currentSessionFactory(tClz);
    SqlSession batchSession = sqlSessionFactory.openSession(ExecutorType.BATCH, Boolean.FALSE);

    String sqlStatement = SqlHelper.table(tClz).getSqlStatement(SqlMethod.INSERT_ONE.getMethod());
    try {
      int i = 0;
      for (T t : list) {
        batchSession.insert(sqlStatement, t);
        if (i >= 1 && i % batchSize == 0) {
          batchSession.flushStatements();
        }
        i++;
      }
      batchSession.flushStatements();
    } finally {
      batchSession.close();
    }

  }

  /**
   * 修改
   */
  public boolean updateById(T obj) {
    return baseMapper.updateById(obj) > 0;
  }

  /**
   * 修改
   */
  public boolean update(T obj, Wrapper<T> qw) {
    return baseMapper.update(obj, qw) > 0;
  }

  /**
   * 主键删除
   */
  public boolean deleteById(Serializable id) {
    Integer affCnt = baseMapper.deleteById(id);
    return affCnt > 0;
  }
  /**
   * 主键删除
   */
  public void deleteByIds(Collection<? extends Serializable> idList) {
    if (CollectionUtils.isEmpty(idList)) {
      log.warn("idList集合为空");
      return;
    }
    baseMapper.deleteBatchIds(idList);
  }

  /**
   * 删除
   */
  public boolean delete(Wrapper<T> qw) {
    Integer affCnt = baseMapper.delete(qw);
    return affCnt > 0;
  }

  /**
   * 删除  不提供Query参数的方法，字段为空的时候容易删除值全表
   */
 /* public boolean delete(Q query) {
    return this.delete(buildQuery(query));
  }*/

  /**
   * ID 取对象,取不到为空
   */
  public T get(Serializable id) {
    return baseMapper.selectById(id);
  }

  /**
   * 查询一条记录
   */
  public T getOne(Q query) {
    return getOne(buildQuery(query));
  }

  /**
   * 查询一条记录
   */
  public T getOne(Wrapper<T> qw) {

    Page<T> p = new Page<>(1, 2);
    // 不发起count查询
    p.setSearchCount(Boolean.FALSE);

    IPage<T> page = baseMapper.selectPage(p, qw);
    if (page.getRecords().isEmpty()) {
      return null;
    }
    int size = page.getRecords().size();
    if (size > 1) {
      log.warn("getOne查询到多条记录,只返回第一条");
    }
    return page.getRecords().get(0);
  }

  /**
   * ID 取对象,取不到对象抛异常
   */
  public T getRequired(Serializable id) {
    return this.getRequired(id, null);
  }

  /**
   * ID 取对象,取不到对象抛异常
   */
  public T getRequired(Serializable id, String errorMsg) {
    T obj = baseMapper.selectById(id);
    if (obj == null) {
      if (StringUtils.isBlank(errorMsg)) {
        throw new ServiceException(CommMsgCode.NO_DATA);
      }
      throw new ServiceException(CommMsgCode.NO_DATA, errorMsg);
    }
    return obj;
  }

  /**
   * count查询
   */
  public int count(Q query) {
    return count(this.buildQuery(query));
  }

  /**
   * count查询
   */
  public int count(Wrapper<T> qw) {
    return baseMapper.selectCount(qw);
  }

  /**
   * 只返回分页列表（不发起count查询）
   */
  public List<T> list(Q query) {
    return list(this.buildQuery(query), query.getPage(), query.getPageSize());
  }

  /**
   * 只返回分页列表（不发起count查询）
   */
  public List<T> list(Wrapper<T> qw, int page, int pageSize) {
    Page<T> p = new Page<>(page, pageSize);
    // 不发起count查询
    p.setSearchCount(Boolean.FALSE);

    return baseMapper.selectPage(p, qw).getRecords();
  }

  /**
   * 返回分页对象
   */
  public PageVO<T> listPage(Q query) {
    return listPage(this.buildQuery(query), query.getPage(), query.getPageSize());
  }

  /**
   * 返回分页对象
   */
  public PageVO<T> listPage(Wrapper<T> qw, int page, int pageSize) {
    Page<T> p = new Page<>(page, pageSize);
    IPage<T> ip = baseMapper.selectPage(p, qw);

    return new PageVO(ip.getRecords(), p);
  }

  /**
   * 查询所有
   */
  public List<T> listAll(Q query) {
    return listAll(this.buildQuery(query));
  }

  /**
   * 查询所有
   */
  public List<T> listAll(Wrapper<T> ew) {
    return baseMapper.selectList(ew);
  }

  /**
   * id 集合查询(返回List)
   */
  public List<T> listByIds(Collection<? extends Serializable> ids) {
    if (CollectionUtils.isEmpty(ids)) {
      log.warn("ids集合为空");
      return Collections.emptyList();
    }
    if (ids.size() > IN_SIZE) {
      log.warn("ids集合大于" + IN_SIZE);
      return Collections.emptyList();
    }
    return baseMapper.selectBatchIds(ids);
  }

  /**
   * id 集合查询(返回Map)
   *
   * @param idClass 主键类型
   */
  public <ID> Map<ID, T> mapByIds(Collection<? extends Serializable> ids, Class<ID> idClass) {
    List<T> list = this.listByIds(ids);
    Map<ID, T> objMap = new HashMap<>(list.size());

    if (list.isEmpty()) {
      return objMap;
    }
    //=== 1.根据对象类型取主键字段
    TableInfo tableInfo = TableInfoHelper.getTableInfo(list.get(0).getClass());
    String idField = tableInfo.getKeyProperty();
    //=== 2.放到map里面减少遍历
    for (T obj : list) {
      try {
        ID idVal;
        String idValStr = BeanUtils.getProperty(obj, idField);
        if (Long.class.isAssignableFrom(idClass)) {
          idVal = (ID) Long.valueOf(idValStr);
        } else if (Integer.class.isAssignableFrom(idClass)) {
          idVal = (ID) Integer.valueOf(idValStr);
        } else {
          idVal = (ID) idValStr;
        }
        objMap.put(idVal, obj);
      } catch (Exception e) {
        throw new ServiceException(CommMsgCode.SERVER_ERROR, e);
      }
    }
    return objMap;
  }

  /**
   * 暴露mapper
   */
  public M getMapper() {
    return this.baseMapper;
  }

  protected Wrapper<T> buildQuery(Q query) {
    return null;
  }


}
